
import pygame

def draw_label(label, width, height, inverse = False, margin_factor = 0.1, border = True, color = [0, 0, 0], font_size = -1):
    if inverse:
        bg_color = color
        fg_color = [255, 255, 255]
    else:
        bg_color = [255, 255, 255]
        fg_color = color
    if width > height:
        margin = height * margin_factor
    else:
        margin = width * margin_factor

    surface = pygame.Surface((width, height))
    surface.fill(bg_color)

    #calculate font so that is will just fit in the label:
    fnt = None
    if font_size == -1:
        fnt_size = 50
        while True:
            fnt = pygame.font.Font(None, fnt_size)
            (txt_width, txt_height) = fnt.size(label)
            if txt_width + margin < width and txt_height + margin < height:
                break
            fnt_size -= 1
            if fnt_size < 1:
                raise Exception("Impossible to draw requested font.")
    else:
        fnt = pygame.font.Font(None, font_size)
        (txt_width, txt_height) = fnt.size(label)

    txt = fnt.render(label, True, fg_color)
    x_offset = (width - txt_width) / 2
    y_offset = (height - txt_height) / 2
    surface.blit(txt, (x_offset, y_offset))

    if border:
        pygame.draw.rect(surface, fg_color, [0, 0, width, height], margin / 2)

    return surface


class Menu(object):

    def __init__(self, offset, size, option_list):
        """
        @param offset A tuple which specifies the upper left corner of the menu (x, y)
        @param size A tuple which specifies the size of the menu (width, height)
        """
        self.__option_list = option_list

        if not len(offset) == 2 or offset[0] < 0 or offset[1] < 0:
            raise Exception("Incorrect offset format.")
        self.__offset = offset

        if not len(size) == 2 or size[0] < 0 or size[1] < 0:
            raise Exception("Incorrect size format.")
        self.__size = size

        self.__option_list = option_list
        self.__surface = None

        self.__margin = 10
        self.__option_height = (self.__size[1] - self.__margin * 2) / len(self.__option_list)
        self.__option_width = self.__size[0] - self.__margin * 2

        self.__refresh = False



    def draw(self, is_forced = False):
        """Instructs the menu to draw its represetation on a surface and return it."""

        #determin mouseover for all menu items:
        is_changed = False
        (m_x, m_y) = pygame.mouse.get_pos()
        for i, option in enumerate(self.__option_list):
            cur_mouseover = (m_x > self.__offset[0]) \
                    and (m_y > (self.__margin + i * self.__option_height)) \
                    and (m_y < (self.__margin + (i + 1) * self.__option_height))
            if not option["mouseover"] == cur_mouseover:
                is_changed = True
            option["mouseover"] = cur_mouseover

        if self.__surface == None or is_changed or is_forced or self.__refresh:
            self.__refresh = False

            self.__surface = pygame.Surface(self.__size)

            pygame.draw.rect(self.__surface, [0, 0, 0], [0, 0, self.__size[0], self.__size[1]])
            pygame.draw.rect(self.__surface, [255, 255, 255], [self.__margin, self.__margin, self.__size[0] - self.__margin * 2, self.__size[1] - self.__margin * 2])

            #draw all menu options:
            for i, option in enumerate(self.__option_list):
                #determin if mouse if over menu option:
                color = [0, 0, 0]
                if not option["enabled"]:
                    color = [100, 100, 100]
                menu_surface = draw_label(option["label"], self.__option_width, self.__option_height, option["mouseover"] and option["enabled"], color = color, font_size = 25)
                self.__surface.blit(menu_surface, (self.__margin, self.__margin + i * self.__option_height))

        return self.__surface


    def event_clicked(self):
        """To be called whenever the left mouse button is pressed."""
        (m_x, m_y) = pygame.mouse.get_pos()
        for i, option in enumerate(self.__option_list):
            if option["enabled"]:
                option["clicked"] = (m_x > self.__offset[0]) \
                        and (m_y > (self.__margin + i * self.__option_height)) \
                        and (m_y < (self.__margin + (i + 1) * self.__option_height))


    def is_clicked(self, option_name):
        for option in self.__option_list:
            if option["name"] == option_name:
                clicked = option["clicked"]
                option["clicked"] = False
                return clicked
        #raise Exception("Could not find requestion option_name.")


    def set_label(self, option_name, label):
        for option in self.__option_list:
            if option["name"] == option_name:
                self.__refresh = True
                option["label"] = label
                return
        raise Exception("Could not find requestion option_name.")


    def set_enabled(self, option_name, enabled):
        for option in self.__option_list:
            if option["name"] == option_name:
                self.__refresh = True
                option["enabled"] = enabled
                return
        #raise Exception("Could not find requestion option_name.")


